package util

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"time"
)

// 通过端口文件获取 listen
//
// @param portPath string 端口文件路径
//
// @param ip string 监听的IP.
// 本地： 127.0.0.1、[::1] ;
// 全部： 0.0.0.0 、[::] 、 空字符串 ;
// 指定IP: 192.168.1.2;
func GetTCPListen(portPath, ip string) (net.Listener, error) {
	var port = 0
	stat, err := os.Stat(portPath)

	// 读取port文件
	if err == nil {
		if stat.IsDir() {
			return nil, fmt.Errorf("port path is not a file: %s ", portPath)
		}
		r, err := os.ReadFile(portPath)
		if err == nil {
			p, err := strconv.Atoi(string(r))
			if err == nil {
				port = p
			}
		}
	}

	var listen net.Listener
	// 使用port参数创建
	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		if port == 0 {
			return nil, err
		}
		// port 被占用，使用随机端口
		l, err := net.Listen("tcp", fmt.Sprintf("%s:", ip))
		if err != nil {
			return nil, err
		}
		listen = l
	} else {
		listen = l
	}

	// 写入端口到文件
	port = listen.Addr().(*net.TCPAddr).Port
	err = os.WriteFile(portPath, []byte(strconv.Itoa(port)), 0666)
	if err != nil {
		listen.Close()
		return nil, err
	}
	return listen, nil
}

// 使用系统浏览器打开网页
func OpenUrl(url string) error {
	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "windows":
		cmd = exec.Command("cmd", "/c", "start", url)
	case "darwin":
		cmd = exec.Command("open", url)
	case "linux":
		cmd = exec.Command("xdg-open", url)
	default:
		return fmt.Errorf("unsupported operating system: %v", runtime.GOARCH)
	}

	return cmd.Start()
}

// 允许电脑使用的DNS
var allowDnsResolverAddress = []string{
	"[2400:3200::1]:53",      // 阿里
	"[2400:3200:baba::1]:53", // 阿里
	"223.5.5.5:53",           // 阿里
	"223.6.6.6:53",           // 阿里
	"114.114.114.114:53",     // 114
	"114.114.115.115:53",     // 114
	"119.29.29.29:53",        // 腾讯
	"[2402:4e00::]:53",       // 腾讯
	"8.8.8.8:53",             // Google
	"8.8.4.4:53",             // Google
	"180.76.76.76:53",        // 百度
}

// 默认DNS，电脑DNS非允许列表中时使用
const defaultResolverAddress = "223.5.5.5:53"

// DNS解析超时时间 (ms)
const dnsResolverTimeout = time.Duration(1*1000) * time.Millisecond

// 校验DNS是否在允许列表，并返回有效的DNS地址
func validDnsResolverAddress(address string) string {
	for _, value := range allowDnsResolverAddress {
		if value == address {
			return address
		}
	}
	return defaultResolverAddress
}

// 自定义的Transport，内容继承 DefaultTransport,进行如下修改
// 1. 禁止使用代理
// 2. 使用指定的DNS地址进行解析(需要使用go1.19版本以上才行,应为在1.19版本修复了windows 无法使用自定义dns的问题)
var CustomDnsWithNoProxyHttpTransport http.RoundTripper = &http.Transport{
	Proxy:                 nil,
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		Resolver: &net.Resolver{
			PreferGo: true,
			Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
				dnsResolverAddress := validDnsResolverAddress(address)
				d := net.Dialer{
					Timeout: dnsResolverTimeout,
				}
				return d.DialContext(ctx, network, dnsResolverAddress)
			},
		},
	}).DialContext,
}
